/**
 * Copyright (c) 2025 ~ 2026 KylinSec Co., Ltd.
 * kiran-authentication-devices is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 *
 * Author: yangfeng <yangfeng@kylinsec.com.cn>
 */

#include <dlfcn.h>
#include <qt5-log-i.h>
#include <unistd.h>

#include "aratekfp.h"
#include "auth-enum.h"
#include "driver/driver-factory.h"
#include "fp-aratek-driver.h"

namespace Kiran
{

#pragma pack(1)
typedef struct
{
    char version[4];
    char manufacture[12];
    char product_name[30];
    char product_model[12];
    char hardware_version[2];
    char bootloader_version[4];
    char firmware_version[4];
    char serial_number[20];
    unsigned short image_width;
    unsigned short image_height;
    char produced_date_time[8];
    unsigned short mode_id;
    unsigned short dpi;
    char isUSBSupported;
    char isUARTSupported;
    char isSPISupported;
    char isSoftwareLicenseInforTrustMe;
    char isSoftwareLicenseInforTrustAFIS;
    char isSoftwareLicenseInforTrustLink;
    char isSoftwareLicenseInforTrustChinaOS;
    char isSoftwareLicenseInforReserved0;
    char isSoftwareLicenseInforReserved1;
    unsigned char reserved[911];
} ARAFP_DevDesc;
#pragma pack()

extern "C"
{
    // libAraTrustFinger.so

    /*
      Initialized Aratek FP device .
      * On sucess it return zero , On fail its return -ve number.
    */
    typedef int (*T_ARAFPSCAN_GlobalInit)(void);

    /*
      Release Aratek FP device.
    */
    typedef int (*T_ARAFPSCAN_GlobalFree)(void);

    /*
      Genrate FP Raw Image Data
    */
    typedef int (*T_ARAFPSCAN_CaptureRawData)(HANDLE nHandle, int nTimeout, unsigned char *pRawData);
    /*
      Genrate BMP data
    */
    typedef int (*T_ARAFPSCAN_CaptureBitmapData)(HANDLE nHandle, int nTimeout, unsigned char *pBmpData);

    typedef int (*T_ARAFPSCAN_GetDeviceDescription)(int deviceIndex, ARAFP_DevDesc *pDeviceDesc);

    typedef int (*T_ARAFPSCAN_GetImageInfo)(HANDLE nHandle, int *pnWidth, int *pnHeight, int *pnDpi);
    /*
      Open FP Device
    */
    typedef int (*T_ARAFPSCAN_OpenDevice)(HANDLE *nHandle, int nIndex);
    /*
       Close Device
    */
    typedef int (*T_ARAFPSCAN_CloseDevice)(HANDLE *nHandle);
    /*
       Get the number of connected FP  Device .
    */
    typedef int (*T_ARAFPSCAN_GetDeviceCount)(int *nDeviceCount);
    /*
       Convert Raw Data To BMP Data .
    */
    typedef int (*T_ARAFPSCAN_RawToBitmap)(unsigned char *pRawData, unsigned char *pBmpData, int nWidth, int nHeight);

    typedef int (*T_ARAFPSCAN_BitmapToRaw)(unsigned char *pBmpData, unsigned char *pRawData, int pnWidth, int pnHeight);

    typedef int (*T_ARAFPSCAN_SetLedStatus)(HANDLE nHandle, int nLedIndex, int pStatus);

    typedef int (*T_ARAFPSCAN_GetLedStatus)(HANDLE nHandle, int nLedIndex, int *pStatus);

    typedef int (*T_ARAFPSCAN_CaptureISOData)(HANDLE nHandle, int nTimeout, int FingerPos, int Imgcompressalg, unsigned char *ISOImgData, int *pLenOutput);

    typedef int (*T_ARAFPSCAN_CaptureANSIData)(HANDLE nHandle, int nTimeout, int FingerPos, int Imgcompressalg, unsigned char *ANSIImgData, int *pLenOutput);

    typedef int (*T_ARAFPSCAN_ExtractFeature)(HANDLE nHandle, unsigned char cFingerPos, unsigned char *pFeatureData);

    typedef int (*T_ARAFPSCAN_ExtractANSIFeature)(HANDLE nHandle, unsigned char cFingerPos, unsigned char *pFeatureData);

    typedef int (*T_ARAFPSCAN_ExtractISOFeature)(HANDLE nHandle, unsigned char cFingerPos, unsigned char *pFeatureData);

    typedef int (*T_ARAFPSCAN_ImgQuality)(int width, int height, unsigned char *ipImage, int *Quality);

    typedef int (*T_ARAFPSCAN_Verify)(HANDLE nHandle, int Security_Level, unsigned char pFeatureData[FEATURE_LENGTH], unsigned char pTemplateData[FEATURE_LENGTH], int *sc, int *pResult);

    typedef int (*T_ARAFPSCAN_GetErrorInfo)(int nErrorNo, char pszErrorInfo[256]);

    typedef int (*T_ARAFPSCAN_BeginLiveCapture)(HANDLE nHandle);

    typedef int (*T_ARAFPSCAN_LiveCaptureRawData)(HANDLE nHandle, unsigned char *pRawData);

    typedef int (*T_ARAFPSCAN_LiveCaptureBitmap)(HANDLE nHandle, unsigned char *pBmpData);

    typedef int (*T_ARAFPSCAN_EndLiveCapture)(HANDLE nHandle);

    typedef int (*T_ARAFPSCAN_VerifyExt)(HANDLE nHandle, int Security_Level, unsigned char pFeatureData[FEATURE_LENGTH], int dbNumFeature, unsigned char *pDbTemplateData /*[dbFeatureNum*FEATURE_LENGTH]*/, int *sc, int *pResult);
    typedef int (*T_ARAFPSCAN_GeneralizeTemplate)(unsigned char *featureBuf, int numFeature, unsigned char *pTemplateDataBuf);
    typedef int (*T_ARAFPSCAN_GetVersion)(unsigned char *versionBuf);

    typedef int (*T_ARAFPSCAN_Enroll)(HANDLE nHandle, unsigned int byTimeout, unsigned char ulCurrentPressTimes, char *userName, char *fingerName);
    typedef int (*T_ARAFPSCAN_Identify)(HANDLE nHandle, int Security_Level, unsigned int byTimeout, char *userName, char *fingerName, int *score);
    typedef int (*T_ARAFPSCAN_DeleteFinger)(HANDLE nHandle, char *userName, char *fingerName);
    typedef int (*T_ARAFPSCAN_DeleteAllFinger)(HANDLE nHandle, char *userName);
    typedef int (*T_ARAFPSCAN_EnrollListFingers)(HANDLE nHandle, char *userName, char *fingerName, int *enrolledNum);
    typedef int (*T_ARAFPSCAN_RenameFinger)(HANDLE nHandle, char *userName, char *oldFingerName, char *newFingerName);
};

struct FPAratekDriverLib
{
    T_ARAFPSCAN_GlobalInit ARAFPSCAN_GlobalInit;
    T_ARAFPSCAN_GlobalFree ARAFPSCAN_GlobalFree;
    T_ARAFPSCAN_CaptureRawData ARAFPSCAN_CaptureRawData;
    T_ARAFPSCAN_CaptureBitmapData ARAFPSCAN_CaptureBitmapData;
    T_ARAFPSCAN_GetDeviceDescription ARAFPSCAN_GetDeviceDescription;
    T_ARAFPSCAN_GetImageInfo ARAFPSCAN_GetImageInfo;
    T_ARAFPSCAN_OpenDevice ARAFPSCAN_OpenDevice;
    T_ARAFPSCAN_CloseDevice ARAFPSCAN_CloseDevice;
    T_ARAFPSCAN_GetDeviceCount ARAFPSCAN_GetDeviceCount;
    T_ARAFPSCAN_RawToBitmap ARAFPSCAN_RawToBitmap;
    T_ARAFPSCAN_BitmapToRaw ARAFPSCAN_BitmapToRaw;
    T_ARAFPSCAN_SetLedStatus ARAFPSCAN_SetLedStatus;
    T_ARAFPSCAN_GetLedStatus ARAFPSCAN_GetLedStatus;
    T_ARAFPSCAN_CaptureISOData ARAFPSCAN_CaptureISOData;
    T_ARAFPSCAN_CaptureANSIData ARAFPSCAN_CaptureANSIData;
    T_ARAFPSCAN_ExtractFeature ARAFPSCAN_ExtractFeature;
    T_ARAFPSCAN_ExtractANSIFeature ARAFPSCAN_ExtractANSIFeature;
    T_ARAFPSCAN_ExtractISOFeature ARAFPSCAN_ExtractISOFeature;
    T_ARAFPSCAN_ImgQuality ARAFPSCAN_ImgQuality;
    T_ARAFPSCAN_Verify ARAFPSCAN_Verify;
    T_ARAFPSCAN_GetErrorInfo ARAFPSCAN_GetErrorInfo;
    T_ARAFPSCAN_BeginLiveCapture ARAFPSCAN_BeginLiveCapture;
    T_ARAFPSCAN_LiveCaptureRawData ARAFPSCAN_LiveCaptureRawData;
    T_ARAFPSCAN_LiveCaptureBitmap ARAFPSCAN_LiveCaptureBitmap;
    T_ARAFPSCAN_EndLiveCapture ARAFPSCAN_EndLiveCapture;
    T_ARAFPSCAN_VerifyExt ARAFPSCAN_VerifyExt;
    T_ARAFPSCAN_GeneralizeTemplate ARAFPSCAN_GeneralizeTemplate;
    T_ARAFPSCAN_GetVersion ARAFPSCAN_GetVersion;
    T_ARAFPSCAN_Enroll ARAFPSCAN_Enroll;
    T_ARAFPSCAN_Identify ARAFPSCAN_Identify;
    T_ARAFPSCAN_DeleteFinger ARAFPSCAN_DeleteFinger;
    T_ARAFPSCAN_DeleteAllFinger ARAFPSCAN_DeleteAllFinger;
    T_ARAFPSCAN_EnrollListFingers ARAFPSCAN_EnrollListFingers;
    T_ARAFPSCAN_RenameFinger ARAFPSCAN_RenameFinger;

    void loadSym(HANDLE libHandle)
    {
        this->ARAFPSCAN_GlobalInit = (T_ARAFPSCAN_GlobalInit)dlsym(libHandle, "ARAFPSCAN_GlobalInit");
        this->ARAFPSCAN_GlobalFree = (T_ARAFPSCAN_GlobalFree)dlsym(libHandle, "ARAFPSCAN_GlobalFree");
        this->ARAFPSCAN_CaptureRawData = (T_ARAFPSCAN_CaptureRawData)dlsym(libHandle, "ARAFPSCAN_CaptureRawData");
        this->ARAFPSCAN_CaptureBitmapData = (T_ARAFPSCAN_CaptureBitmapData)dlsym(libHandle, "ARAFPSCAN_CaptureBitmapData");
        this->ARAFPSCAN_GetDeviceDescription = (T_ARAFPSCAN_GetDeviceDescription)dlsym(libHandle, "ARAFPSCAN_GetDeviceDescription");
        this->ARAFPSCAN_GetImageInfo = (T_ARAFPSCAN_GetImageInfo)dlsym(libHandle, "ARAFPSCAN_GetImageInfo");
        this->ARAFPSCAN_OpenDevice = (T_ARAFPSCAN_OpenDevice)dlsym(libHandle, "ARAFPSCAN_OpenDevice");
        this->ARAFPSCAN_CloseDevice = (T_ARAFPSCAN_CloseDevice)dlsym(libHandle, "ARAFPSCAN_CloseDevice");
        this->ARAFPSCAN_GetDeviceCount = (T_ARAFPSCAN_GetDeviceCount)dlsym(libHandle, "ARAFPSCAN_GetDeviceCount");
        this->ARAFPSCAN_RawToBitmap = (T_ARAFPSCAN_RawToBitmap)dlsym(libHandle, "ARAFPSCAN_RawToBitmap");
        this->ARAFPSCAN_BitmapToRaw = (T_ARAFPSCAN_BitmapToRaw)dlsym(libHandle, "ARAFPSCAN_BitmapToRaw");
        this->ARAFPSCAN_SetLedStatus = (T_ARAFPSCAN_SetLedStatus)dlsym(libHandle, "ARAFPSCAN_SetLedStatus");
        this->ARAFPSCAN_GetLedStatus = (T_ARAFPSCAN_GetLedStatus)dlsym(libHandle, "ARAFPSCAN_GetLedStatus");
        this->ARAFPSCAN_CaptureISOData = (T_ARAFPSCAN_CaptureISOData)dlsym(libHandle, "ARAFPSCAN_CaptureISOData");
        this->ARAFPSCAN_CaptureANSIData = (T_ARAFPSCAN_CaptureANSIData)dlsym(libHandle, "ARAFPSCAN_CaptureANSIData");
        this->ARAFPSCAN_ExtractFeature = (T_ARAFPSCAN_ExtractFeature)dlsym(libHandle, "ARAFPSCAN_ExtractFeature");
        this->ARAFPSCAN_ExtractANSIFeature = (T_ARAFPSCAN_ExtractANSIFeature)dlsym(libHandle, "ARAFPSCAN_ExtractANSIFeature");
        this->ARAFPSCAN_ExtractISOFeature = (T_ARAFPSCAN_ExtractISOFeature)dlsym(libHandle, "ARAFPSCAN_ExtractISOFeature");
        this->ARAFPSCAN_ImgQuality = (T_ARAFPSCAN_ImgQuality)dlsym(libHandle, "ARAFPSCAN_ImgQuality");
        this->ARAFPSCAN_Verify = (T_ARAFPSCAN_Verify)dlsym(libHandle, "ARAFPSCAN_Verify");
        this->ARAFPSCAN_GetErrorInfo = (T_ARAFPSCAN_GetErrorInfo)dlsym(libHandle, "ARAFPSCAN_GetErrorInfo");
        this->ARAFPSCAN_BeginLiveCapture = (T_ARAFPSCAN_BeginLiveCapture)dlsym(libHandle, "ARAFPSCAN_BeginLiveCapture");
        this->ARAFPSCAN_LiveCaptureRawData = (T_ARAFPSCAN_LiveCaptureRawData)dlsym(libHandle, "ARAFPSCAN_LiveCaptureRawData");
        this->ARAFPSCAN_LiveCaptureBitmap = (T_ARAFPSCAN_LiveCaptureBitmap)dlsym(libHandle, "ARAFPSCAN_LiveCaptureBitmap");
        this->ARAFPSCAN_EndLiveCapture = (T_ARAFPSCAN_EndLiveCapture)dlsym(libHandle, "ARAFPSCAN_EndLiveCapture");
        this->ARAFPSCAN_VerifyExt = (T_ARAFPSCAN_VerifyExt)dlsym(libHandle, "ARAFPSCAN_VerifyExt");
        this->ARAFPSCAN_GeneralizeTemplate = (T_ARAFPSCAN_GeneralizeTemplate)dlsym(libHandle, "ARAFPSCAN_GeneralizeTemplate");
        this->ARAFPSCAN_GetVersion = (T_ARAFPSCAN_GetVersion)dlsym(libHandle, "ARAFPSCAN_GetVersion");
        this->ARAFPSCAN_Enroll = (T_ARAFPSCAN_Enroll)dlsym(libHandle, "ARAFPSCAN_Enroll");
        this->ARAFPSCAN_Identify = (T_ARAFPSCAN_Identify)dlsym(libHandle, "ARAFPSCAN_Identify");
        this->ARAFPSCAN_DeleteFinger = (T_ARAFPSCAN_DeleteFinger)dlsym(libHandle, "ARAFPSCAN_DeleteFinger");
        this->ARAFPSCAN_DeleteAllFinger = (T_ARAFPSCAN_DeleteAllFinger)dlsym(libHandle, "ARAFPSCAN_DeleteAllFinger");
        this->ARAFPSCAN_EnrollListFingers = (T_ARAFPSCAN_EnrollListFingers)dlsym(libHandle, "ARAFPSCAN_EnrollListFingers");
        this->ARAFPSCAN_RenameFinger = (T_ARAFPSCAN_RenameFinger)dlsym(libHandle, "ARAFPSCAN_RenameFinger");

        this->isLoaded = true;
    }

    bool isLoaded = false;
};

REGISTER_DRIVER(FINGERPRINT_ARATEK_DRIVER_NAME, FPAratekDriver);

FPAratekDriver::FPAratekDriver(QObject *parent) : Driver(parent),
                                                  m_libHandle(nullptr),
                                                  m_deviceHandle(nullptr)
{
    m_driverLib.reset(new FPAratekDriverLib);
    setName(FINGERPRINT_ARATEK_DRIVER_NAME);
}

FPAratekDriver::~FPAratekDriver()
{
    if (m_libHandle)
    {
        dlclose(m_libHandle);
        m_libHandle = nullptr;
    }
    if (m_deviceHandle)
    {
        closeDevice();
        m_deviceHandle = nullptr;
    }
}

bool FPAratekDriver::initDriver(const QString &libPath)
{
    KLOG_INFO() << "FPAratekDriver initDriver";

    return loadLibrary(FP_ARATEK_DRIVER_LIB);
}

bool FPAratekDriver::loadLibrary(const QString &libPath)
{
    // 打开指定的动态链接库文件；立刻决定返回前接触所有未决定的符号。若打开错误返回NULL，成功则返回库引用
    m_libHandle = dlopen(libPath.toStdString().c_str(), RTLD_NOW);
    if (m_libHandle == nullptr)
    {
        KLOG_ERROR() << "Load libzkfp failed,error:" << dlerror();

        return false;
    }

    m_driverLib->loadSym(m_libHandle);
    return true;
}

bool FPAratekDriver::isLoaded()
{
    return m_driverLib->isLoaded;
}

int FPAratekDriver::openDevice()
{
    KLOG_INFO() << "FPAratekDriver openDevice";
    int ret = m_driverLib->ARAFPSCAN_GlobalInit();
    if (ret != GEN_SUCCEDED)
    {
        KLOG_ERROR() << "ARAFPSCAN_GlobalInit failed"
                     << ret;
        return ret;
    }
    ret = m_driverLib->ARAFPSCAN_OpenDevice(&m_deviceHandle, 0);
    if (ret != GEN_SUCCEDED)
    {
        KLOG_ERROR() << "ARAFPSCAN_OpenDevice failed"
                     << ret;
        return ret;
    }
    ret = m_driverLib->ARAFPSCAN_GetImageInfo(m_deviceHandle, &m_width, &m_height, &m_dpi);
    if (ret != GEN_SUCCEDED)
    {
        KLOG_ERROR() << "ARAFPSCAN_GetImageInfo failed" << ret;
        return ret;
    }
    return ret;
}
int FPAratekDriver::closeDevice()
{
    int ret = m_driverLib->ARAFPSCAN_CloseDevice(&m_deviceHandle);
    if (ret != GEN_SUCCEDED)
    {
        KLOG_ERROR() << "ARAFPSCAN_CloseDevice failed:" << ret;
        return ret;
    }
    ret = m_driverLib->ARAFPSCAN_GlobalFree();
    if (ret != GEN_SUCCEDED)
    {
        KLOG_ERROR() << "ARAFPSCAN_GlobalFree failed:" << ret;
        return ret;
    }

    m_deviceHandle = nullptr;

    return ret;
}

bool FPAratekDriver::isDeviceOpened()
{
    return m_deviceHandle != nullptr;
}

int FPAratekDriver::acquireFeature(unsigned char *fea)
{
    KLOG_INFO() << "acquireFeature, w h:" << m_width << m_height;

    int ret = 0;
    int score = 0;
    unsigned char *rawdata = (unsigned char *)malloc(m_width * m_height);

    for (int retryCnt = 0; retryCnt < ENROLL_RETRYCNT; retryCnt++)
    {
        usleep(1000000);
        ret = m_driverLib->ARAFPSCAN_CaptureRawData(m_deviceHandle, 0, rawdata);
        if (ret != 0)
        {
            KLOG_ERROR() << "ARAFPSCAN_CaptureRawData error:" << ret;
            continue;
        }

        m_driverLib->ARAFPSCAN_ImgQuality(m_width, m_height, rawdata, &score);
        if (score < IMAGEQUALITY_THRESHOLD)
        {
            KLOG_ERROR() << "ARAFPSCAN_ImgQuality failed, score:" << score;
            continue;
        }
        KLOG_INFO() << "ARAFPSCAN_ImgQuality OK, score:" << score;

        break;
    }
    if (ret != 0)
    {
        free(rawdata);
        return ret;
    }

    if (score < IMAGEQUALITY_THRESHOLD)
    {
        free(rawdata);
        return IMAGEQUALITY_BAD_CODE;
    }

    ret = m_driverLib->ARAFPSCAN_ExtractFeature(m_deviceHandle, 0, fea);
    if (ret != 0)
    {
        KLOG_ERROR() << "ARAFPSCAN_ExtractFeature error:" << ret;
    }
    free(rawdata);
    return ret;
}

int FPAratekDriver::templateMatch(QByteArray fpTemplate1, QByteArray fpTemplate2)
{
    int score = 0, pResult = 0;
    m_driverLib->ARAFPSCAN_VerifyExt(m_deviceHandle,
                                     1,
                                     (unsigned char *)fpTemplate1.data(),
                                     1,
                                     (unsigned char *)fpTemplate2.data(), &score, &pResult);

    return pResult;
}

}  // namespace Kiran
